package com.wantong.voice.webrtc;

import cn.hutool.core.io.FileUtil;

import java.io.*;

/**
 * AudioGainUtil pcm音频放大
 *
 * @author : stan
 * @version : 1.0
 * @date :  2022/4/5 10:56 AM
 **/
public class AudioGainUtils {
    public static void main(String[] args) throws IOException {
        File f = new File("/Users/stan/Desktop/0.pcm");
        byte[] bytes = gainPcm(f, 16, 5.0f);
        FileUtil.writeBytes(bytes, new File("/Users/stan/Desktop/0_2_gain.pcm"));
    }


    /**
     * 放大pcm 只写了单声道 双声道的话 buffer.len 采样率*2
     *
     * @param sample 采样率 16bit = 2 byte
     * @param rate   放大倍率 1.0
     * @return
     */
    public static byte[] gainPcm(byte[] bytes, int sample, float rate) throws IOException {
        if (bytes == null) {
            throw new RuntimeException("bytes null");
        }

        if (sample < 8) {
            sample = 8;
        }

        if (sample % 8 != 0) {
            throw new RuntimeException("采样率必须是8的倍数");
        }

        if (rate < 0 || rate == 1.0f) {
            return bytes;
        }

        //双声道在这里*2
        int bfLen = sample / 8;
        byte[] buffer = new byte[bfLen];
        try (InputStream is = new ByteArrayInputStream(bytes)) {
            try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
                while(true) {
                    if (is.available() >= bfLen) {
                        is.read(buffer);
                        os.write(rateByte(buffer, bfLen, rate));
                    } else {
                        // log.warn 这文件有点问题吧
                        break;
                    }
                }

                return os.toByteArray();
            }
        }
    }
    public static byte[] gainPcm(File file, int sample, float rate) throws IOException {
        if (!file.exists()) {
            throw new RuntimeException("文件不存在");
        }

        return gainPcm(FileUtil.readBytes(file), sample, rate);
    }

    /**
     * 倍率计算
     *
     * @param buffer
     * @param bfLen
     * @param rate
     * @return
     */
    private static byte[] rateByte(byte[] buffer, int bfLen, float rate) {
        //1. 有符号进制转换
        //2. 放大倍数
        //3. 回写字节集
        if (bfLen == 2) {
            short val = toShort(buffer);
            val *= rate;

            return toBytes(val);
        }

        if (bfLen == 4) {
            int val = toInt(buffer);
            val *= rate;

            return toBytes(val);
        }

        if (bfLen == 8) {
            long val = toLong(buffer);
            val *= rate;

            return toBytes(val);
        }

        throw new RuntimeException("不支持的类型 len: " + bfLen);
    }

    public static byte[] toBytes(long val) {
        long tmp = val;
        byte[] b = new byte[8];
        for (int i = 0; i < b.length; i++) {
            b[i] = Long.valueOf(tmp & 0xff).byteValue();
            tmp = tmp >> 8;
        }

        return b;
    }

    public static byte[] toBytes(int val) {
        long tmp = val;
        byte[] b = new byte[4];
        for (int i = 0; i < b.length; i++) {
            b[i] = Long.valueOf(tmp & 0xff).byteValue();
            tmp = tmp >> 8;
        }

        return b;
    }

    public static byte[] toBytes(short val) {
        long tmp = val;
        byte[] b = new byte[2];
        for (int i = 0; i < b.length; i++) {
            b[i] = Long.valueOf(tmp & 0xff).byteValue();
            tmp = tmp >> 8;
        }

        return b;
    }

    public static long toLong(byte[] b) {
        return (b[0] & 0xFF)
                | ((b[1] & 0xFF) << 8)
                | ((b[2] & 0xFF) << 16)
                | ((long) (b[3] & 0xFF) << 24)
                | ((long) (b[4] & 0xFF) << 32)
                | ((long) (b[5] & 0xFF) << 40)
                | ((long) (b[6] & 0xFF) << 48)
                | ((long) (b[7] & 0xFF) << 56);
    }

    public static int toInt(byte[] b) {
        return (b[0] & 0xFF)
                | ((b[1] & 0xFF) << 8)
                | ((b[2] & 0xFF) << 16)
                | ((b[3] & 0xFF) << 24);
    }

    public static short toShort(byte[] b) {
        return (short) ((b[0] & 0xFF) | ((b[1] & 0xFF) << 8));
    }

}
